using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Mysoft.Dbf.Writers;

namespace Mysoft.Dbf
{
    public class DBFExporter<TExportSpecification>
    {
        private ContentWriter contentWriter = new ContentWriter(typeof(TExportSpecification));
        private DateTime _lastModifiedDate = DateTime.Now;
        public DateTime LastModifiedDate
        {
            get { return _lastModifiedDate; }
            set { _lastModifiedDate = value; }
        }
        
        public Stream GetStream()
        {
            Stream rez = new MemoryStream();
            WriteToStream(rez);
            return rez;
        }

        public void ExportToFile(string fileName)
        {
            Stream rez = new FileStream(fileName, FileMode.Create);
            WriteToStream(rez);
            rez.Flush();
            rez.Close();
        }

        private void WriteToStream(Stream toStream)
        {
            WriteFileHeader(toStream);
            contentWriter.WriteHeader(toStream, 1, new NamingParameters());
            WriteHeaderTerminator(toStream);
            WriteRows(toStream);
        }

        private List<TExportSpecification> _rows = new List<TExportSpecification>();
        public List<TExportSpecification> Rows
        {
            get { return _rows; }
        }

        private readonly string NotDeletedMark = " ";
        private void WriteRows(Stream toStream)
        {
            if (Rows.Count == 0)
                return;
            StreamWriter streamWriter = new StreamWriter(toStream, System.Text.Encoding.GetEncoding("gb2312"));
            foreach (TExportSpecification row in this.Rows)
            {
                streamWriter.Write(NotDeletedMark);
                contentWriter.WriteValue(row, streamWriter);
            }
            streamWriter.Write((char)26);
            streamWriter.Flush();
        }

        private void WriteHeaderTerminator(Stream rez)
        {
            rez.WriteByte(0x0D);
        }

        private void WriteFileHeader(Stream toStream)
        {
            BinaryWriter wr = new BinaryWriter(toStream);
            DBFHeader hdr = GetDBFHeader();
            wr.Write(hdr.DbfVersion);
            wr.Write(hdr.Year);
            wr.Write(hdr.Month);
            wr.Write(hdr.Day);
            wr.Write(hdr.RecordCount);
            wr.Write(hdr.HeaderSize);
            wr.Write(hdr.RecordSize);
            wr.Write(hdr.Reserved1);
            wr.Write(hdr.Trans);
            wr.Write(hdr.Encrypt);
            wr.Write(hdr.Reserved2_1);
            wr.Write(hdr.Reserved2_2);
            wr.Write(hdr.Reserved2_3);
            wr.Write(hdr.MDX);
            wr.Write(hdr.LangId);
            wr.Write(hdr.Reserved3);
        }

        private DBFHeader GetDBFHeader()
        {
            DBFHeader rez = new DBFHeader();
            rez.DbfVersion = 0x03;
            rez.Day = (byte) this.LastModifiedDate.Day;
            rez.Month = (byte)this.LastModifiedDate.Month;
            rez.Year = (byte)(this.LastModifiedDate.Year % 100);
            rez.Encrypt = 0;
            rez.HeaderSize = GetHeaderSize() ;
            rez.LangId = 0;
            rez.MDX = 0;
            rez.RecordCount = this.Rows.Count;
            rez.RecordSize = (short) (1 + contentWriter.FieldTotalLength);
            rez.Reserved1 = 0;
            rez.Trans = 0;
            rez.Reserved2_1 = 0;
            rez.Reserved2_2 = 0;
            rez.Reserved2_3 = 0;
            rez.Reserved3 = 0;

            return rez;
        }

        struct DBFHeader
        {
            public byte DbfVersion;
            public byte Year;
            public byte Month;
            public byte Day;
            public int RecordCount;
            public short HeaderSize;
            public short RecordSize;
            public short Reserved1;
            public byte Trans;
            public byte Encrypt;
            public long Reserved2_1;
            public short Reserved2_2;
            public short Reserved2_3;
            public byte MDX;
            public byte LangId;
            public short Reserved3;
        }

        private short GetHeaderSize()
        {
            return (short) (33 + 32 * this.contentWriter.FieldCount);
        }

    }
}
